package de.projectgoodmorning.alarm.storage;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.util.Log;

import com.db4o.ObjectSet;

import de.projectgoodmorning.alarm.model.gateway.CombinedAlarm;
import de.projectgoodmorning.alarm.model.generic.Alarm;
import de.projectgoodmorning.alarm.util.AlarmFactory;

/**
 * Der AlarmStorage ist die Schnittstelle zu den persistent gespeicherten
 * Alarmen. Diese Klasse ist dafuer zustaendig alle Alarme dauerhaft zu speichern,
 * neue Alarme mit aufzunehmen und gespeicherte Alarme zurueck zugeben.
 *
 * Der AlarmStorage uebernimmt ebenfalls die Aufgabe der ID-Verteilung fuer die
 * hinzugefuegten Alarme.
 * 
 * Der AlarmStorage ist soweit spezialisiert, dass er CombinedAlarm-Instanzen 
 * erwartet. Dies ermoeglicht eine komfortablere Benutzung.
 */
public class AlarmStorage {
	private static final String TAG = AlarmStorage.class.getCanonicalName();
	
	private static final String STORAGE_FILE = "alarmstorage.db";

	private List<CombinedAlarm> list;
	private Context context;
	private Db4oHelper dbAccess;
	private IdCounter counter ;

	/**
	 * erstellt eine Instanz, die den Zugriff auf die gespeicherten
	 * Alarme ermoeglicht. 
	 * @param context	benoetigt zur Speicherung der Datenbank
	 */
	public AlarmStorage( Context context ) {
		list = new ArrayList<CombinedAlarm>();
		this.context = context;
		this.counter = new IdCounter( context );
		
		dbAccess = new Db4oHelper( context, STORAGE_FILE );
		loadAlarms();
	}

	/**
	 * laedt alle gespeicherten Alarme aus der Datenbank neu.
	 * Zugriff auf die aktualisierte Liste der Alarme unter 
	 * {@link AlarmStorage#toArray()}
	 * <br />
	 * <br />
	 * <b>Achtung:</b><br />
	 * Sollten keine Alarme vorhanden sein, wird ein Default-Alarm erstellt.
	 */
	public void loadAlarms() {
		ObjectSet<Object> result = dbAccess.db().queryByExample( CombinedAlarm.class );
		list.clear();
		
		while( result.hasNext() ) {
			CombinedAlarm alarm = ( CombinedAlarm ) result.next();
			list.add( alarm );
		}
		
		if( counter.latestID() == 0 ) {
			add( AlarmFactory.createGoodMorningAlarm( context ) );
			add( AlarmFactory.createTechnoMorningAlarm( context ) );
		}
	}

	/**
	 * fuegt den uebergebenen Alarm in die Liste und speichert diesen
	 * dauerhaft in der Datenbank.
	 * @param alarm		zuspeichender Alarm 
	 */
	public void add( CombinedAlarm alarm ) {
		if( alarm != null ) {
			alarm.id = counter.nextID();
			
			alarm.getMainAlarm().id = counter.nextID();
			for( Alarm a : alarm.getGatewayAlarms() ) {
				a.id = counter.nextID();
			}
			
			list.add( alarm );
			dbAccess.db().store( alarm );
			dbAccess.db().commit();
			
			Log.i( TAG, "Alarm added to storage - ID: " + alarm.id );
		}
	}

	/**
	 * entfernt einen Alarm anhand seines Index.
	 * @param index		erwartet gueltigen Index
	 */
	public void remove( int index ) {
		remove( list.get( index ) );
	}

	/**
	 * entfernt einen Alarm anhand seiner ID.
	 * @param id	erwartet die ID des Alarms
	 */
	public void removeById( int id ) {
		for( CombinedAlarm ca : list ) {
			if( ca.id == id ) {
				remove( ca );
				return;
			}
		}
	}

	private void remove( CombinedAlarm alarm ) {
		if( alarm != null ) {
			list.remove( alarm );
			dbAccess.db().delete( alarm );
			dbAccess.db().commit();
			
			Log.i( TAG, "Alarm removed from storage - ID: " + alarm.id );
		}
	}
	
	/**
	 * gibt einen Alarm anhand des Listen-Index zurueck.
	 * @param index		erwartet einen gueltigen Index.
	 * @return			gibt CombineAlarm oder null zurueck
	 */
	public CombinedAlarm get( int index ) {
		loadAlarms();
		try {
			return list.get( index );
		} catch (Exception e) {
			Log.i( TAG, "Alarm not found by index: " + index );
		}
		return null;
	}
	
	/**
	 * gibt den gesuchten Alarm anhand seiner ID zurueck 
	 * @param id	erwartet die ID des CombinedAlarms
	 * @return		gibt gefundenen CombinedAlarm zurueck oder 
	 * 				null wenn Objekt nicht gefunden werden konnte
	 */
	public CombinedAlarm getById( int id ) {
		loadAlarms();
		for( CombinedAlarm ca : list ) {
			if( ca.id == id ) {
				return ca;
			}
		}
		return null;
	}

	/**
	 * gibt alle gespeicherten Alarme als Array zurueck.
	 * @return		alle CombinedAlarme als Array		
	 */
	public CombinedAlarm[] toArray() {
		loadAlarms();
		return list.toArray( new CombinedAlarm[list.size()] );
	}
	
	/**
	 * entfernt alle gespeicherten Alarme aus der Datenbank
	 */
	public void removeAll() {
		dbAccess.close();
		String path = context.getDir( "data", Context.MODE_PRIVATE ) + "/";
		if( new File( path + STORAGE_FILE ).delete() ) {
			Log.i( TAG, "All alarms removed from storage" );
		}
	}
}
